use std::{fs, sync::Arc};

use crate::{executor::conf::get_managers, extend::WorkflowFinishCallBack};

use super::error::{Error, Result};
use arc_swap::ArcSwapOption;
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use shq_common::prelude::{mysql::exec_sqls, get_work_path, ExprParser};

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Workflow {
    //流程配置ID，WFID
    pub id: u32,
    //流程配置名称
    pub name: String,
    #[serde(default)]
    //流程配置描述
    pub desc: Option<String>,
    //流程配置版本
    pub version: u8,
    //流程配置对应菜单ID
    pub perm_id: u32,
    //同一个用户是否只审批一次（前面审批过的用户在后面的节点中出现，系统会自动提交）
    #[serde(default)]
    pub(crate) is_sign_once: bool,
    //流程数据表的名称
    pub table_name: String,
    //流程数据表中的标题字段名称
    pub title_col_names: Vec<String>,
    //流程数据表中的流程运行ID字段名称
    #[serde(default = "run_id_default")]
    pub(crate) flow_run_id_col: String,
    //流程运行完成时执行的回调函数
    #[serde(default)]
    pub(crate) finish_call_back: Option<CallBack>,
    //流程管理员，可以激活流程和强制提交
    #[serde(default)]
    pub(crate) managers: Vec<Executor>,
    //流程步骤
    pub(crate) steps: Vec<Step>,
    //是否有历史表
    #[serde(default)]
    pub(crate) has_history_table: bool,
    //是否可以创建流程
    #[serde(default="true_default")]
    pub can_create: bool,
}
fn true_default() -> bool {
    true
}
fn run_id_default() -> String {
    "FRID".to_owned()
}
impl Workflow {
    pub async fn is_manager(&self, user_id: u32) -> anyhow::Result<bool> {
        let ids=get_managers(self.id, self.version).await?;
        let is_ok = ids.iter().any(|u| *u == user_id);
        Ok(is_ok)
    }
}
impl Workflow {
    pub(crate) async fn parse(&mut self) -> Result<()> {
        if self.steps.is_empty() {
            return Err(Error::from_conf(
                self.id,
                self.name.as_str(),
                self.version,
                "流程步骤不能为空".to_owned(),
            ));
        }
        if !self.steps[0].back_to_steps.is_empty() {
            return Err(Error::from_conf(
                self.id,
                self.name.as_str(),
                self.version,
                "第一个步骤不能配置回退步骤".to_owned(),
            ));
        }
        if self.steps[self.steps.len() - 1].can_take_back {
            return Err(Error::from_conf(
                self.id,
                self.name.as_str(),
                self.version,
                "最后一个步骤不能取回".to_owned(),
            ));
        }
        for e in self.steps[0].executors.iter() {
            if e.r#type == ExecutorType::Func {
                return Err(Error::from_conf(
                    self.id,
                    self.name.as_str(),
                    self.version,
                    "流程第一个步骤的执行人不能配置为Func",
                ));
            }
        }
        for e in self.managers.iter() {
            if e.r#type == ExecutorType::Func {
                return Err(Error::from_conf(
                    self.id,
                    self.name.as_str(),
                    self.version,
                    "流程管理员不能配置为Func",
                ));
            }
        }
        for (i, s1) in self.steps.iter().enumerate() {
            if (s1.id - (i as u8)) != 1 {
                return Err(Error::from_conf(
                    self.id,
                    self.name.as_str(),
                    self.version,
                    format!("步骤id:{}有误，id必须从1开始，并且连续", s1.id),
                ));
            }
            for (j, s2) in self.steps.iter().enumerate() {
                if s1.id == s2.id && i != j {
                    return Err(Error::from_conf(
                        self.id,
                        self.name.as_str(),
                        self.version,
                        format!("步骤id:{}重复", s1.id),
                    ));
                }
                if s1.name == s2.name && i != j {
                    return Err(Error::from_conf(
                        self.id,
                        self.name.as_str(),
                        self.version,
                        format!("步骤name:{}重复", s1.name),
                    ));
                }
            }
        }
        for s in self.steps.iter_mut() {
            s.parse(&self.table_name).await.or_else(|err| {
                return Err(Error::from_conf(
                    self.id,
                    self.name.as_str(),
                    self.version,
                    err.to_string(),
                ));
            })?;
        }
        Ok(())
    }
    pub fn get_step(&self, step_id: u8) -> Option<&Step> {
        self.steps.iter().find(|s| s.id == step_id)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub(crate) struct CallBack {
    r#type: CallBackType,
    content: String,
}
impl CallBack {
    pub(crate) async fn exec(
        &self,
        run_id: u64,
        user_id: u32,
        tx: &mut sqlx::Transaction<'_, sqlx::MySql>,
    ) -> Result<()> {
        match self.r#type {
            CallBackType::SqlStr => {
                let sqls = self.content.as_str();
                let params_names = &["run_id", "user_id"];
                let rid = run_id.to_string();
                let uid = user_id.to_string();
                let params_vals = &[rid.as_str(), uid.as_str()];
                exec_sqls(sqls, params_names, params_vals, tx).await?;
            }
            CallBackType::SqlFile => {
                let file_name = format!("{}/sqls/{}.sql", get_work_path(), self.content);
                let sqls = fs::read_to_string(&file_name)
                    .or_else(|err| Err(anyhow::anyhow!("读取文件{file_name}失败:{err}")))?;
                let params_names = &["run_id", "user_id"];
                let rid = run_id.to_string();
                let uid = user_id.to_string();
                let params_vals = &[rid.as_str(), uid.as_str()];
                exec_sqls(&sqls, params_names, params_vals, tx).await?;
            }
            CallBackType::Func => {
                let wfcb = WorkflowFinishCallBack::new(&self.content);
                wfcb.exec(run_id, user_id, tx).await?;
            }
        }
        Ok(())
    }
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub(crate) enum CallBackType {
    SqlStr,
    SqlFile,
    Func,
}
//节点没有执行人时的策略
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub(crate) enum NoExecutorTactics {
    //抛出异常
    Err,
    //自动提交
    Auto,
    //由流程管理员提交
    Manager,
}
impl Default for NoExecutorTactics {
    fn default() -> Self {
        NoExecutorTactics::Err
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Step {
    //步骤id
    pub id: u8,
    //步骤名称
    pub name: String,
    //步骤执行人，可以配置多个
    pub(crate) executors: Vec<Executor>,
    //会签类型，默认为任意一个用户审批通过即可
    #[serde(default)]
    pub(crate) counter_sign_type: CounterSignType,
    //是否需要投票，同意者大于不同意者则提交，否则退回到back_to_steps指定节点。back_to_steps没有指定退回到发起人
    #[serde(default)]
    pub(crate) is_vote: bool,
    //可以退回到的步骤IDS，默认为空
    #[serde(default)]
    pub back_to_steps: Vec<u8>,
    //是否可以取回，默认不可以
    #[serde(default)]
    pub can_take_back: bool,
    //是否可以挂起，默认不可以
    #[serde(default)]
    pub can_suspend: bool,
    //进入条件。默认为空；不符合条件则跳过
    #[serde(default)]
    pub(crate) where_cause: Option<String>,
    //where_clause的解析器
    #[serde(skip)]
    pub(crate) expr_parser: Option<ExprParser>,
    //步骤完成前执行的回调函数；一般用于验证数据是否有效
    #[serde(default)]
    pub(crate) check_call_back: Option<String>,
    //节点没有执行人时的策略
    #[serde(default)]
    pub(crate) no_executor_tractics: NoExecutorTactics,
}
impl Step {
    async fn parse(&mut self, table_name: &str) -> anyhow::Result<()> {
        if self.is_vote {
            if self.back_to_steps.is_empty() {
                return Err(anyhow::anyhow!(
                    "步骤{}配置了投票，但是没有配置退回步骤",
                    self.name
                ));
            }
            if self.back_to_steps.len() > 1 {
                return Err(anyhow::anyhow!(
                    "步骤{}配置了投票，back_to_steps只能有一个",
                    self.name
                ));
            }
            if self.counter_sign_type == CounterSignType::Any {
                return Err(anyhow::anyhow!(
                    "步骤{}配置了投票，但是会签类型为Any，无法投票",
                    self.name
                ));
            }
            if self.can_suspend {
                return Err(anyhow::anyhow!(
                    "步骤{}配置了投票，但是可以中止，无法投票",
                    self.name
                ));
            }
        } else {
            if !self.back_to_steps.is_empty() {
                if self.counter_sign_type != CounterSignType::Any {
                    return Err(anyhow::anyhow!(
                        "步骤{}配置了退回，并且不是投票节点，会签类型必须是Any",
                        self.name
                    ));
                }
            }
        }
        if self.executors.is_empty() {
            return Err(anyhow::anyhow!("步骤{}没有配置执行人", self.name));
        }
        for btid in self.back_to_steps.iter() {
            if btid > &self.id {
                return Err(anyhow::anyhow!(
                    "步骤{}的退回步骤id:{}有误，退回步骤id必须小于当前步骤id",
                    self.name,
                    btid
                ));
            }
        }
        if let Some(ccb) = &self.where_cause {
            let ep = ExprParser::from(ccb, table_name).await?;
            self.expr_parser = Some(ep);
        }
        for executor in self.executors.iter_mut() {
            executor.parse(table_name).await?;
        }
        Ok(())
    }
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub(crate) struct Executor {
    //执行人类型，角色、用户、函数
    pub(crate) r#type: ExecutorType,
    #[serde(default)]
    //执行人IDS，角色ID或者用户ID
    pub(crate) ids: Vec<u32>,
    #[serde(default)]
    //函数名称
    pub(crate) func_name: Option<String>,
    #[serde(default)]
    //限定到某个步骤执行人相同部门的人员
    pub(crate) organ_from_step: Option<u8>,
    //参与条件。默认为空
    #[serde(default)]
    pub(crate) where_cause: Option<String>,
    //where_clause的解析器
    #[serde(skip)]
    pub(crate) expr_parser: Option<ExprParser>,
}
impl Executor {
    async fn parse(&mut self, table_name: &str) -> anyhow::Result<()> {
        if let Some(wc) = &self.where_cause {
            let ep = ExprParser::from(wc, table_name).await?;
            self.expr_parser = Some(ep);
        }
        Ok(())
    }
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub(crate) enum ExecutorType {
    // 角色
    Role,
    // 管理员
    User,
    // 超级管理员
    Func,
}
impl Default for ExecutorType {
    fn default() -> Self {
        ExecutorType::Role
    }
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub(crate) enum CounterSignType {
    //任意一个用户审批通过即可
    Any,
    //所有用户审批通过才可
    All,
    //所有部门中有一个用户审批通过才可
    AllDept,
}
impl Default for CounterSignType {
    fn default() -> Self {
        CounterSignType::Any
    }
}
static CONF: Lazy<ArcSwapOption<&'static Vec<Workflow>>> =
    Lazy::new(|| ArcSwapOption::const_empty());
pub async fn init_workflow_config() -> Result<()> {
    let work_path = get_work_path();
    let file_path = format!("{}/config_files/workflow.yaml", work_path);
    let conf_str = fs::read_to_string(&file_path)
        .or_else(|err| Err(anyhow::anyhow!("读取配置文件{file_path}失败:{}", err)))?;
    let mut conf: Vec<Workflow> = serde_yaml::from_str(&conf_str)
        .or_else(|err| Err(anyhow::anyhow!("serde_yaml解析配置文件失败:{}", err)))?;
    for (i, wf1) in conf.iter().enumerate() {
        for (j, wf2) in conf.iter().enumerate() {
            if i != j && wf1.id == wf2.id && wf1.version == wf2.version {
                return Err(Error::from_conf(
                    wf1.id,
                    wf1.name.as_str(),
                    wf1.version,
                    "id重复",
                ));
            }
            if i != j && wf1.name == wf2.name && wf1.version == wf2.version {
                return Err(Error::from_conf(
                    wf1.id,
                    wf1.name.as_str(),
                    wf1.version,
                    "name重复",
                ));
            }
        }
    }
    for w in conf.iter_mut() {
        w.parse().await?;
    }
    let conf_ref = Box::leak(Box::new(conf));
    CONF.store(Some(Arc::new(conf_ref)));
    Ok(())
}
pub fn get_workflow_config(wfid: u32, version: u8) -> Option<&'static Workflow> {
    let confs = CONF.load().clone();
    for conf in confs.unwrap().iter() {
        if conf.id == wfid && conf.version == version {
            return Some(conf);
        }
    }
    None
}
pub fn get_workflow_newest_config(wfid: u32) -> Option<(&'static Workflow, u8)> {
    let confs = CONF.load().clone();
    let mut conf_opt: Option<&'static Workflow> = None;
    let mut max_version: u8 = 0;
    for conf in confs.unwrap().iter() {
        if conf.id == wfid {
            if let Some(pre) = conf_opt {
                if pre.version < conf.version {
                    conf_opt = Some(conf);
                    max_version = conf.version;
                }
            } else {
                conf_opt = Some(conf);
                max_version = conf.version;
            }
        }
    }
    conf_opt.map(|conf| (conf, max_version))
}
